Render this report with
~/spinal_cord_paper/scripts/Gg_devel_scWGCNA_module_analysis_render.sh.
library(Seurat)
Attaching SeuratObject
library(WGCNA)
Loading required package: dynamicTreeCut
Loading required package: fastcluster
Attaching package: ‘fastcluster’
The following object is masked from ‘package:stats’:
hclust
Attaching package: ‘WGCNA’
The following object is masked from ‘package:stats’:
cor
library(tidyr)
library(ggplot2)
library(stringr)
library(patchwork)
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ readr 1.4.0 ✔ forcats 0.5.1
✔ purrr 0.3.4
── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
library(cowplot)
Attaching package: ‘cowplot’
The following object is masked from ‘package:patchwork’:
align_plots
library(pheatmap)
source("~/Neuraltube/scripts/heatmap4.R")
Load individual seurat and test WGCNA data
The individual data sets are the Day 5 (Gg_D05_ctrl_seurat_070323),
Day 7 (Gg_D07_ctrl_seurat_070323), and Day 10 (Gg_ctrl_1_seurat_070323)
chicken spinal cord sets. The test WGCNA data are the modules calculated
on the integrated data set of all three stages.
se_path <- c("Gg_D05_ctrl_seurat_070323",
"Gg_D07_ctrl_seurat_070323",
"Gg_ctrl_1_seurat_070323")
clust_col <- read.csv("~/spinal_cord_paper/annotations/broad_cluster_marker_colors.csv") %>%
rename(broad = broad_cluster) %>%
select(-marker)
Order of the broad clusters for plotting purposes.
broad_order <- c("progenitors",
"FP",
"RP",
"FP/RP",
"neurons",
"OPC",
"MFOL",
"pericytes",
"microglia",
"blood",
"vasculature"
)
my.ses <- list()
col_table <- list()
ord_levels <- list()
for (i in seq(se_path)) {
# load the data sets
my.se <- readRDS(paste0("~/spinal_cord_paper/data/", se_path[i], ".rds"))
annot <- read.csv(list.files("~/spinal_cord_paper/annotations",
pattern = str_remove(se_path[i], "_seurat_\\d{6}"),
full.names = TRUE))
if(length(table(annot$number)) != length(table(my.se$seurat_clusters))) {
stop("Number of clusters must be identical!")
}
# rename for left join
annot <- annot %>%
mutate(fine = paste(fine, number, sep = "_")) %>%
mutate(number = factor(number, levels = 1:nrow(annot))) %>%
rename(seurat_clusters = number)
# cluster order for vln plots
ord_levels[[i]] <- annot$fine[order(match(annot$broad, broad_order))]
# create index for color coding
col_table[[i]] <- annot %>%
left_join(clust_col, by = "broad") %>%
select(c("fine", "color"))
# add cluster annotation to meta data
my.se@meta.data <- my.se@meta.data %>%
rownames_to_column("rowname") %>%
left_join(annot, by = "seurat_clusters") %>%
mutate(fine = factor(fine, levels = annot$fine)) %>%
column_to_rownames("rowname")
my.ses[[i]] <- my.se
}
names(my.ses) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(col_table) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
names(ord_levels) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
rm(my.se, annot)
# The reference WGCNA data. We can have several, if we want to test many at the same time
WGCNA_data = list()
WGCNA_data[[1]] = readRDS("~/spinal_cord_paper/output/Gg_devel_int_scWGCNA_250723.rds")
my.wsub =list()
my.wsub[[1]]= c(1:22)
# the name of each sample, as they appear in my.files and in the metadata of the combined object
my.samplenames = c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
# The subset of clusters in each of the corresponding samples
my.subset=list(c(1:24),
c(1:26),
c(1:22))
# This is just to add a little bit more sense to the modules, so that we don't get just a color. Corresponds to WGCNA_data
my.modulenames = list()
my.modulenames[[1]] = c(1:22)
#Subset the seurat objects if needed as defined above
for (i in 1:length(my.ses)) {
my.ses[[i]] = subset(my.ses[[i]], idents = my.subset[[i]])
}
# Take only genes that are present in the all samples
my.dcs = list()
# We go trhough each WGCNA data
for(i in 1:length(WGCNA_data)) {
#We start with complete modules
my.dc = WGCNA_data[[i]]$dynamicCols
#Remove the few genes that are not found in the other datasets
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[1]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[2]]@assays$RNA@data))]
my.dc = my.dc[which(names(my.dc) %in% rownames(my.ses[[3]]@assays$RNA@data))]
my.dc = my.dc[which(my.dc %in% names(table(WGCNA_data[[i]]$dynamicCols))[my.wsub[[i]]])]
my.dcs[[i]] = my.dc
}
Module gene correlation
Here, we do a correlation matrix / heatmap, to see which cell
clusters group togheter. This helps us to make more detailed
dotplots.
This part of the script can still be used to compare several WGCNA
datasets in parallel.
# broad cluster color table
all_col <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1, 11)) %>%
mutate(sample_celltype = paste(sample, fine, sep = "_")) %>%
select(c("color", "sample_celltype", "sample"))
# take the normalized data, of each single-cell object.
my.datExpr = list()
# Go trough each seurat object
for (i in 1:length(my.ses)) {
my.datExpr[[i]] = data.frame(my.ses[[i]]@assays$RNA@data)
}
# Calculate, for every cell, in every sample, the average expression of each of the modules.
my.MEs = list()
# For each set of WGCNA modules
for (i in 1:length(WGCNA_data)) {
# Make a new list
my.MEs[[i]] = list()
# Populate with data frames from each seurat object
for (j in 1:length(my.ses)) {
# Data frame of average module expression, per cell.
my.MEs[[i]][[j]] = moduleEigengenes(t(my.datExpr[[j]][names(my.dcs[[i]]),]), colors = my.dcs[[i]])
}
}
#Calculate average of expression, per sample and cell cluster
my.modavg = list()
for (i in 1:length(WGCNA_data)) {
my.modavg[[i]] = list()
for (j in 1:length(my.ses)) {
#Make the mean per cell clusters
my.modavg[[i]][[j]] = aggregate(
my.MEs[[i]][[j]]$averageExpr,
list(my.ses[[j]]@meta.data$seurat_clusters),
mean
)
# Give the cell clusters meaningful names
rownames(my.modavg[[i]][[j]]) = paste0(
my.samplenames[j],
"_",
levels(my.ses[[j]]@meta.data$fine)[my.subset[[j]]]
)
}
}
#Gather data to plot
my.wgmat = list()
for (i in 1:length(WGCNA_data)) {
#bind the expression data into one dataframe
my.wgmat[[i]] = data.table::rbindlist(my.modavg[[i]])
#Get rid of the extra column
my.wgmat[[i]] = data.frame(my.wgmat[[i]][,-1])
#Restore the rownames
rownames(my.wgmat[[i]]) = unlist(sapply(my.modavg[[i]], rownames))
}
#Get a dataframe with annotations for all the samples and colors we need.
my.metam <- list()
for (i in seq(my.ses)) {
my.metam[[i]] <- my.ses[[i]][[]]
rownames(my.metam[[i]]) <- paste0(rownames(my.metam[[i]]), "_", i)
}
my.metam <- do.call(rbind, my.metam)
my.metam$orig.ident <- str_replace_all(my.metam$orig.ident, pattern = "Gg_ctrl_1", "Gg_D10_ctrl")
# my.metam$sample_celltype = paste0(substr(my.metam$orig.ident,7,9),"_",my.metam$seurat_clusters)
my.metam$sample_celltype = paste0(my.metam$orig.ident, "_", my.metam$fine)
my.metam <- my.metam %>%
tibble::rownames_to_column(var = "cell_ID") %>%
dplyr::left_join(all_col, by = "sample_celltype") %>%
tibble::column_to_rownames(var = "cell_ID")
# get sample colors
my.colsm = c("grey", "grey30", "black")
names(my.colsm) <- c("Gg_D05_ctrl", "Gg_D07_ctrl", "Gg_D10_ctrl")
#The colors for the samples and clusters. First the closest to the heatmap. Add a white space to easy the eye and make less confussing
my.heatcols =list()
for (i in 1:length(my.wgmat)) {
my.heatcols[[i]] = as.matrix(data.frame(
cluster= as.character(all_col$color[match(rownames(my.wgmat[[i]]), all_col$sample_celltype)]),
"." = "white",
sample= as.character(my.colsm[match(all_col$sample, names(my.colsm))]))
)
}
rm(my.datExpr, my.MEs, my.dcs)
spearman correlation heatmap
annotations
# names and colors for the heatmap annotation
annot_name <- data.frame(
"Celltypes" = all_col$sample_celltype,
"Sample" = all_col$sample,
row.names = all_col$sample_celltype)
pheat_col_table <- do.call(rbind, col_table) %>%
rownames_to_column("sample") %>%
mutate(sample = substr(sample, 1,11)) %>%
mutate(fine = paste(sample, fine, sep = "_"))
# match color table with annotation
pheat_col_table <- pheat_col_table[match(annot_name$Celltypes, pheat_col_table$fine),]
annot_col <- list(
Celltypes = pheat_col_table$color,
Sample = c(Gg_D05_ctrl = "#A4A4A4",
Gg_D07_ctrl = "#515151",
Gg_D10_ctrl = "#000000")
)
names(annot_col[[1]]) <- annot_name$Celltypes

heatmap of module pseudobulk average expression

Load the integrated data set
The integrated data set on which the WGCNA is calculated. We plot it,
split by the original cell types from the three original samples.
my.sec = readRDS("~/spinal_cord_paper/data/Gg_devel_int_seurat_250723.rds")
identical(rownames(my.metam), colnames(my.sec))
[1] TRUE
#Which WGCNA dataset are we using?
my.W = 1
#Choose how many groups of cell clusters we want to use. Based on the distance clustering from above
my.clcl = cutree(hclust(as.dist(1-cor(t(my.wgmat[[my.W]])))), k = 5)
my.metam$sample_celltype <- factor(my.metam$sample_celltype, levels = names(my.clcl))
#Set the identities of the integrated data, to the annotated clusters
my.sec = SetIdent(my.sec, value = my.metam$sample_celltype)
p1 <- DimPlot(
my.sec,
reduction = "tsne",
label = TRUE,
repel = TRUE,
cols = my.heatcols[[1]][,"cluster"],
split.by = "orig.ident"
) +
NoLegend()
p1

Avg. module exp. by stage
tSNE DimPlots showing the average expression of each module by
stage.
for (i in seq(my.ses)) {
# prepare average expression table
tmp <- avg.mod.eigengenes %>%
tibble::rownames_to_column("cell_ID") %>%
dplyr::filter(grepl(paste0("_", i, "$"), cell_ID)) %>%
dplyr::mutate(cell_ID = stringr::str_remove_all(cell_ID, paste0("_", i))) %>%
tibble::column_to_rownames("cell_ID")
identical(rownames(tmp), colnames(my.ses[[i]]))
# add meta data to the seurat objects
my.ses[[i]] <- AddMetaData(my.ses[[i]], tmp)
}
#max and min expression per module (column max)
mod_max <- apply(avg.mod.eigengenes, MARGIN = 2, FUN = max)[module_order]
Error: object 'module_order' not found
AE over time
We plot the average expression of each module in the three stages and
the 5 broad cell type clusters present in all 3 stages.
VlnPlots of avg. module exp. by stage and seurat cluster
colored by module


for (i in seq(my.ses)) {
my.ses[[i]]$seurat_clusters <- factor(
my.ses[[i]]$seurat_clusters,
levels = levels(my.ses[[i]]$seurat_clusters)[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
)
}
vplots <- list()
for (i in seq(my.ses)) {
mods <- colnames(my.ses[[i]][[]])[grep("^AE",colnames(my.ses[[i]][[]]))]
vplots[[i]] <- VlnPlot(
my.ses[[i]],
features = mods[module_order],
group.by = "seurat_clusters",
stack = TRUE, flip = TRUE,
cols = substring(mods, 3)[module_order]) +
theme(legend.position = "none") +
geom_hline(yintercept = 0, lty = "dashed")
}
vplots[[1]]
vplots[[2]]
vplots[[3]]

colored by cell type
col_table[[i]]$color[as.integer(str_extract(ord_levels[[i]], "\\d{1,2}$"))]
[1] "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#edc919" "#853335" "#99ca3c" "#cd2b91" "#cd2b91" "#cd2b91" "#cd2b91" "#008cb5" "#008cb5" "#008cb5" "#008cb5"
[17] "#008cb5" "#008cb5" "#333399" "#333399" "#f26a2d" "#bebebe"
MN modules
For the figures we specifically select the two MN modules and plot
them as Vln plots.


avg. module exp. by stage
v1 <- VlnPlot(my.sec,
features = mods[module_order],
group.by = "orig.ident")
v1
pdf("~/spinal_cord_paper/figures/Fig_1_AE_by_stage.pdf", height = 20, width = 20)
v1
module GO terms barplots
# Load the top 50 (by limma::topGO) Go term table list from scWGCNA_Gg_devel_int.Rmd
goterms <- readRDS("~/spinal_cord_paper/output/Gg_devel_int_module_GOTerms_250723.rds")
# Load the devel module annotations
modules <- read.csv("~/spinal_cord_paper/annotations/Gg_devel_int_scWGCNA_module_annotation.csv")
# Set list names as module number and name (e.g. "1_black")
names(goterms) <- modules$module
plot_list <- list()
for (i in seq(goterms)) {
# Select the darkred module (motor neuron/transmembrane transport)
toplot <- goterms[[i]]
terms <- as.character(toplot$Term)
#Plot the barplot!
plot_list[[i]] <- ggplot(toplot, aes(x=Term, y=-log10(P.DE), fill=Ont)) +
geom_bar(stat="identity") +
scale_fill_manual(values = str_remove(names(goterms)[i], "^\\d{1,2}_")) +
theme(axis.text.x = element_text(angle = 90, hjust = 1),
axis.text.y = element_text(size = 5)) +
labs(title=paste0("Top GOTerm of module ", names(goterms)[i])) +
coord_flip() +
geom_hline(yintercept = -log10(0.05), lty = "dashed") +
theme_cowplot()
}
rm(toplot, terms)
names(plot_list) <- modules$module
# Export PDF
pdf("~/spinal_cord_paper/figures/GO_term_barplot_scWGCNA_Gg_devel_modules.pdf", width = 14, height = 7)
for (i in seq(goterms)) {
grid.arrange(plot_list[[i]])
}
dev.off()
# Date and time of Rendering
Sys.time()
sessionInfo()
LS0tCnRpdGxlOiAiRGV2ZWxfaW50IFdHQ05BIG1vZHVsZXMgZXhwcmVzc2lvbiBpbiBOVCBENSwgRDcsIGFuZCBEMTAgc2FtcGxlcyIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDQuMDguMjAyMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KClJlbmRlciB0aGlzIHJlcG9ydCB3aXRoIH4vc3BpbmFsX2NvcmRfcGFwZXIvc2NyaXB0cy9HZ19kZXZlbF9zY1dHQ05BX21vZHVsZV9hbmFseXNpc19yZW5kZXIuc2guCgpgYGB7ciBzZXR1cH0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoV0dDTkEpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShncmlkRXh0cmEpCnNvdXJjZSgifi9OZXVyYWx0dWJlL3NjcmlwdHMvaGVhdG1hcDQuUiIpCgpgYGAKCiMgTG9hZCBpbmRpdmlkdWFsIHNldXJhdCBhbmQgdGVzdCBXR0NOQSBkYXRhCgpUaGUgaW5kaXZpZHVhbCBkYXRhIHNldHMgYXJlIHRoZSBEYXkgNSAoR2dfRDA1X2N0cmxfc2V1cmF0XzA3MDMyMyksIERheSA3IChHZ19EMDdfY3RybF9zZXVyYXRfMDcwMzIzKSwgYW5kIERheSAxMCAoR2dfY3RybF8xX3NldXJhdF8wNzAzMjMpIGNoaWNrZW4gc3BpbmFsIGNvcmQgc2V0cy4gVGhlIHRlc3QgV0dDTkEgZGF0YSBhcmUgdGhlIG1vZHVsZXMgY2FsY3VsYXRlZCBvbiB0aGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvZiBhbGwgdGhyZWUgc3RhZ2VzLgoKYGBge3IgZGF0YS1zZXRzfQpzZV9wYXRoIDwtIGMoIkdnX0QwNV9jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX0QwN19jdHJsX3NldXJhdF8wNzAzMjMiLAogICAgICAgICAgICAgIkdnX2N0cmxfMV9zZXVyYXRfMDcwMzIzIikKCmNsdXN0X2NvbCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9icm9hZF9jbHVzdGVyX21hcmtlcl9jb2xvcnMuY3N2IikgJT4lIAogIHJlbmFtZShicm9hZCA9IGJyb2FkX2NsdXN0ZXIpICU+JSAKICBzZWxlY3QoLW1hcmtlcikKCmBgYAoKT3JkZXIgb2YgdGhlIGJyb2FkIGNsdXN0ZXJzIGZvciBwbG90dGluZyBwdXJwb3Nlcy4KCmBgYHtyIG9yZGVyaW5nfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKYGBgCgoKYGBge3Igc2V1cmF0LW9iamVjdHMtYW5kLWFubm90YXRpb25zfQpteS5zZXMgPC0gbGlzdCgpCmNvbF90YWJsZSA8LSBsaXN0KCkKb3JkX2xldmVscyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoc2VfcGF0aCkpIHsKICAjIGxvYWQgdGhlIGRhdGEgc2V0cwogIG15LnNlIDwtIHJlYWRSRFMocGFzdGUwKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvIiwgc2VfcGF0aFtpXSwgIi5yZHMiKSkKICBhbm5vdCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKHNlX3BhdGhbaV0sICJfc2V1cmF0X1xcZHs2fSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUpKQogIAogIGlmKGxlbmd0aCh0YWJsZShhbm5vdCRudW1iZXIpKSAhPSBsZW5ndGgodGFibGUobXkuc2Ukc2V1cmF0X2NsdXN0ZXJzKSkpIHsKICAgICBzdG9wKCJOdW1iZXIgb2YgY2x1c3RlcnMgbXVzdCBiZSBpZGVudGljYWwhIikKICB9CiAgCiAgIyByZW5hbWUgZm9yIGxlZnQgam9pbgogIGFubm90IDwtIGFubm90ICU+JSAKICAgIG11dGF0ZShmaW5lID0gcGFzdGUoZmluZSwgbnVtYmVyLCBzZXAgPSAiXyIpKSAlPiUgCiAgICBtdXRhdGUobnVtYmVyID0gZmFjdG9yKG51bWJlciwgbGV2ZWxzID0gMTpucm93KGFubm90KSkpICU+JSAKICAgIHJlbmFtZShzZXVyYXRfY2x1c3RlcnMgPSBudW1iZXIpIAogIAogICMgY2x1c3RlciBvcmRlciBmb3IgdmxuIHBsb3RzCiAgb3JkX2xldmVsc1tbaV1dIDwtIGFubm90JGZpbmVbb3JkZXIobWF0Y2goYW5ub3QkYnJvYWQsIGJyb2FkX29yZGVyKSldCiAgCiAgIyBjcmVhdGUgaW5kZXggZm9yIGNvbG9yIGNvZGluZwogIGNvbF90YWJsZVtbaV1dIDwtIGFubm90ICU+JQogICAgbGVmdF9qb2luKGNsdXN0X2NvbCwgYnkgPSAiYnJvYWQiKSAlPiUgCiAgICBzZWxlY3QoYygiZmluZSIsICJjb2xvciIpKQogIAogICMgYWRkIGNsdXN0ZXIgYW5ub3RhdGlvbiB0byBtZXRhIGRhdGEKICBteS5zZUBtZXRhLmRhdGEgPC0gbXkuc2VAbWV0YS5kYXRhICU+JSAKICAgIHJvd25hbWVzX3RvX2NvbHVtbigicm93bmFtZSIpICU+JSAKICAgIGxlZnRfam9pbihhbm5vdCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gYW5ub3QkZmluZSkpICU+JSAKICAgIGNvbHVtbl90b19yb3duYW1lcygicm93bmFtZSIpCiAgCiAgbXkuc2VzW1tpXV0gPC0gbXkuc2UKCn0KCm5hbWVzKG15LnNlcykgPC0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQpuYW1lcyhjb2xfdGFibGUpIDwtIGMoIkdnX0QwNV9jdHJsIiwgIkdnX0QwN19jdHJsIiwgIkdnX0QxMF9jdHJsIikKbmFtZXMob3JkX2xldmVscykgPC0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQoKcm0obXkuc2UsIGFubm90KQpgYGAKCmBgYHtyIGlucHV0IGRhdGEsIGVjaG89VFJVRX0KIyBUaGUgcmVmZXJlbmNlIFdHQ05BIGRhdGEuIFdlIGNhbiBoYXZlIHNldmVyYWwsIGlmIHdlIHdhbnQgdG8gdGVzdCBtYW55IGF0IHRoZSBzYW1lIHRpbWUKV0dDTkFfZGF0YSA9IGxpc3QoKQpXR0NOQV9kYXRhW1sxXV0gPSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9HZ19kZXZlbF9pbnRfc2NXR0NOQV8yNTA3MjMucmRzIikKbXkud3N1YiA9bGlzdCgpCm15LndzdWJbWzFdXT0gYygxOjIyKQoKIyB0aGUgbmFtZSBvZiBlYWNoIHNhbXBsZSwgYXMgdGhleSBhcHBlYXIgaW4gbXkuZmlsZXMgYW5kIGluIHRoZSBtZXRhZGF0YSBvZiB0aGUgY29tYmluZWQgb2JqZWN0Cm15LnNhbXBsZW5hbWVzID0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQoKIyBUaGUgc3Vic2V0IG9mIGNsdXN0ZXJzIGluIGVhY2ggb2YgdGhlIGNvcnJlc3BvbmRpbmcgc2FtcGxlcwpteS5zdWJzZXQ9bGlzdChjKDE6MjQpLAogICAgICAgICAgICAgICBjKDE6MjYpLAogICAgICAgICAgICAgICBjKDE6MjIpKQoKIyBUaGlzIGlzIGp1c3QgdG8gYWRkIGEgbGl0dGxlIGJpdCBtb3JlIHNlbnNlIHRvIHRoZSBtb2R1bGVzLCBzbyB0aGF0IHdlIGRvbid0IGdldCBqdXN0IGEgY29sb3IuIENvcnJlc3BvbmRzIHRvIFdHQ05BX2RhdGEKbXkubW9kdWxlbmFtZXMgPSBsaXN0KCkKbXkubW9kdWxlbmFtZXNbWzFdXSA9IGMoMToyMikKCmBgYAoKYGBge3IgcHJlLXByb2Nlc3MsIGVjaG89VFJVRX0KCiNTdWJzZXQgdGhlIHNldXJhdCBvYmplY3RzIGlmIG5lZWRlZCBhcyBkZWZpbmVkIGFib3ZlCmZvciAoaSBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiAgbXkuc2VzW1tpXV0gPSBzdWJzZXQobXkuc2VzW1tpXV0sIGlkZW50cyA9IG15LnN1YnNldFtbaV1dKQp9CgojIFRha2Ugb25seSBnZW5lcyB0aGF0IGFyZSBwcmVzZW50IGluIHRoZSBhbGwgc2FtcGxlcwpteS5kY3MgPSBsaXN0KCkKCiMgV2UgZ28gdHJob3VnaCBlYWNoIFdHQ05BIGRhdGEKZm9yKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAjV2Ugc3RhcnQgd2l0aCBjb21wbGV0ZSBtb2R1bGVzCiAgbXkuZGMgPSBXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMKICAKICAjUmVtb3ZlIHRoZSBmZXcgZ2VuZXMgdGhhdCBhcmUgbm90IGZvdW5kIGluIHRoZSBvdGhlciBkYXRhc2V0cwogIG15LmRjID0gbXkuZGNbd2hpY2gobmFtZXMobXkuZGMpICVpbiUgcm93bmFtZXMobXkuc2VzW1sxXV1AYXNzYXlzJFJOQUBkYXRhKSldCiAgbXkuZGMgPSBteS5kY1t3aGljaChuYW1lcyhteS5kYykgJWluJSByb3duYW1lcyhteS5zZXNbWzJdXUBhc3NheXMkUk5BQGRhdGEpKV0KICBteS5kYyA9IG15LmRjW3doaWNoKG5hbWVzKG15LmRjKSAlaW4lIHJvd25hbWVzKG15LnNlc1tbM11dQGFzc2F5cyRSTkFAZGF0YSkpXQogIAogIG15LmRjID0gbXkuZGNbd2hpY2gobXkuZGMgJWluJSBuYW1lcyh0YWJsZShXR0NOQV9kYXRhW1tpXV0kZHluYW1pY0NvbHMpKVtteS53c3ViW1tpXV1dKV0KICAKICBteS5kY3NbW2ldXSA9IG15LmRjCiAgCn0KYGBgCgojIE1vZHVsZSBnZW5lIGNvcnJlbGF0aW9uCgpIZXJlLCB3ZSBkbyBhIGNvcnJlbGF0aW9uIG1hdHJpeCAvIGhlYXRtYXAsIHRvIHNlZSB3aGljaCBjZWxsIGNsdXN0ZXJzIGdyb3VwIHRvZ2hldGVyLiBUaGlzIGhlbHBzIHVzIHRvIG1ha2UgbW9yZSBkZXRhaWxlZCBkb3RwbG90cy4gIApUaGlzIHBhcnQgb2YgdGhlIHNjcmlwdCBjYW4gc3RpbGwgYmUgdXNlZCB0byBjb21wYXJlIHNldmVyYWwgV0dDTkEgZGF0YXNldHMgaW4gcGFyYWxsZWwuICAKCmBgYHtyfQojIGJyb2FkIGNsdXN0ZXIgY29sb3IgdGFibGUKYWxsX2NvbCA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwgMTEpKSAlPiUgCiAgbXV0YXRlKHNhbXBsZV9jZWxsdHlwZSA9IHBhc3RlKHNhbXBsZSwgZmluZSwgc2VwID0gIl8iKSkgJT4lIAogIHNlbGVjdChjKCJjb2xvciIsICJzYW1wbGVfY2VsbHR5cGUiLCAic2FtcGxlIikpCmBgYAoKYGBge3IgbW9kdWxlX2V4cHJlc3Npb25fZGF0YSwgZWNobz1UUlVFLCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MjB9CgojIHRha2UgdGhlIG5vcm1hbGl6ZWQgZGF0YSwgb2YgZWFjaCBzaW5nbGUtY2VsbCBvYmplY3QuCm15LmRhdEV4cHIgPSBsaXN0KCkKCiMgR28gdHJvdWdoIGVhY2ggc2V1cmF0IG9iamVjdApmb3IgKGkgaW4gMTpsZW5ndGgobXkuc2VzKSkgewogbXkuZGF0RXhwcltbaV1dID0gZGF0YS5mcmFtZShteS5zZXNbW2ldXUBhc3NheXMkUk5BQGRhdGEpCn0KCiMgQ2FsY3VsYXRlLCBmb3IgZXZlcnkgY2VsbCwgaW4gZXZlcnkgc2FtcGxlLCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggb2YgdGhlIG1vZHVsZXMuCm15Lk1FcyA9IGxpc3QoKQoKIyBGb3IgZWFjaCBzZXQgb2YgV0dDTkEgbW9kdWxlcwpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICAjIE1ha2UgYSBuZXcgbGlzdAogIG15Lk1Fc1tbaV1dID0gbGlzdCgpCiAgCiAgIyBQb3B1bGF0ZSB3aXRoIGRhdGEgZnJhbWVzIGZyb20gZWFjaCBzZXVyYXQgb2JqZWN0CiAgZm9yIChqIGluIDE6bGVuZ3RoKG15LnNlcykpIHsKICAgIAogICAgIyBEYXRhIGZyYW1lIG9mIGF2ZXJhZ2UgbW9kdWxlIGV4cHJlc3Npb24sIHBlciBjZWxsLgogICAgbXkuTUVzW1tpXV1bW2pdXSA9IG1vZHVsZUVpZ2VuZ2VuZXModChteS5kYXRFeHByW1tqXV1bbmFtZXMobXkuZGNzW1tpXV0pLF0pLCBjb2xvcnMgPSBteS5kY3NbW2ldXSkKICB9Cn0KCiNDYWxjdWxhdGUgYXZlcmFnZSBvZiBleHByZXNzaW9uLCBwZXIgc2FtcGxlIGFuZCBjZWxsIGNsdXN0ZXIKbXkubW9kYXZnID0gbGlzdCgpCgpmb3IgKGkgaW4gMTpsZW5ndGgoV0dDTkFfZGF0YSkpIHsKICAKICBteS5tb2RhdmdbW2ldXSA9IGxpc3QoKQogIAogIGZvciAoaiBpbiAxOmxlbmd0aChteS5zZXMpKSB7CiAgICAKICAgICNNYWtlIHRoZSBtZWFuIHBlciBjZWxsIGNsdXN0ZXJzCiAgICBteS5tb2RhdmdbW2ldXVtbal1dID0gYWdncmVnYXRlKAogICAgICBteS5NRXNbW2ldXVtbal1dJGF2ZXJhZ2VFeHByLAogICAgICBsaXN0KG15LnNlc1tbal1dQG1ldGEuZGF0YSRzZXVyYXRfY2x1c3RlcnMpLAogICAgICBtZWFuCiAgICAgICkKICAgICMgR2l2ZSB0aGUgY2VsbCBjbHVzdGVycyBtZWFuaW5nZnVsIG5hbWVzCiAgICByb3duYW1lcyhteS5tb2RhdmdbW2ldXVtbal1dKSA9IHBhc3RlMCgKICAgICAgbXkuc2FtcGxlbmFtZXNbal0sCiAgICAgICJfIiwKICAgICAgbGV2ZWxzKG15LnNlc1tbal1dQG1ldGEuZGF0YSRmaW5lKVtteS5zdWJzZXRbW2pdXV0KICAgICAgKQogICAgfQp9CgoKCiNHYXRoZXIgZGF0YSB0byBwbG90CgpteS53Z21hdCA9IGxpc3QoKQoKZm9yIChpIGluIDE6bGVuZ3RoKFdHQ05BX2RhdGEpKSB7CiAgCiAgI2JpbmQgdGhlIGV4cHJlc3Npb24gZGF0YSBpbnRvIG9uZSBkYXRhZnJhbWUKICBteS53Z21hdFtbaV1dID0gZGF0YS50YWJsZTo6cmJpbmRsaXN0KG15Lm1vZGF2Z1tbaV1dKQogICNHZXQgcmlkIG9mIHRoZSBleHRyYSBjb2x1bW4KICBteS53Z21hdFtbaV1dID0gZGF0YS5mcmFtZShteS53Z21hdFtbaV1dWywtMV0pCiAgI1Jlc3RvcmUgdGhlIHJvd25hbWVzCiAgcm93bmFtZXMobXkud2dtYXRbW2ldXSkgPSB1bmxpc3Qoc2FwcGx5KG15Lm1vZGF2Z1tbaV1dLCByb3duYW1lcykpCgp9CgojR2V0IGEgZGF0YWZyYW1lIHdpdGggYW5ub3RhdGlvbnMgZm9yIGFsbCB0aGUgc2FtcGxlcyBhbmQgY29sb3JzIHdlIG5lZWQuCm15Lm1ldGFtIDwtIGxpc3QoKQoKZm9yIChpIGluIHNlcShteS5zZXMpKSB7CiAgbXkubWV0YW1bW2ldXSA8LSBteS5zZXNbW2ldXVtbXV0KICByb3duYW1lcyhteS5tZXRhbVtbaV1dKSA8LSBwYXN0ZTAocm93bmFtZXMobXkubWV0YW1bW2ldXSksICJfIiwgaSkKfQpteS5tZXRhbSA8LSBkby5jYWxsKHJiaW5kLCBteS5tZXRhbSkKCm15Lm1ldGFtJG9yaWcuaWRlbnQgPC0gc3RyX3JlcGxhY2VfYWxsKG15Lm1ldGFtJG9yaWcuaWRlbnQsIHBhdHRlcm4gPSAgIkdnX2N0cmxfMSIsICJHZ19EMTBfY3RybCIpCgojIG15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSA9IHBhc3RlMChzdWJzdHIobXkubWV0YW0kb3JpZy5pZGVudCw3LDkpLCJfIixteS5tZXRhbSRzZXVyYXRfY2x1c3RlcnMpCm15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSA9IHBhc3RlMChteS5tZXRhbSRvcmlnLmlkZW50LCAiXyIsIG15Lm1ldGFtJGZpbmUpCgpteS5tZXRhbSA8LSBteS5tZXRhbSAlPiUgCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4odmFyID0gImNlbGxfSUQiKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKGFsbF9jb2wsIGJ5ID0gInNhbXBsZV9jZWxsdHlwZSIpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJjZWxsX0lEIikKCgojIGdldCBzYW1wbGUgY29sb3JzCm15LmNvbHNtID0gYygiZ3JleSIsICJncmV5MzAiLCAiYmxhY2siKQpuYW1lcyhteS5jb2xzbSkgPC0gYygiR2dfRDA1X2N0cmwiLCAiR2dfRDA3X2N0cmwiLCAiR2dfRDEwX2N0cmwiKQogCiNUaGUgY29sb3JzIGZvciB0aGUgc2FtcGxlcyBhbmQgY2x1c3RlcnMuIEZpcnN0IHRoZSBjbG9zZXN0IHRvIHRoZSBoZWF0bWFwLiBBZGQgYSB3aGl0ZSBzcGFjZSB0byBlYXN5IHRoZSBleWUgYW5kIG1ha2UgbGVzcyBjb25mdXNzaW5nCm15LmhlYXRjb2xzID1saXN0KCkKZm9yIChpIGluIDE6bGVuZ3RoKG15LndnbWF0KSkgewogIG15LmhlYXRjb2xzW1tpXV0gPSBhcy5tYXRyaXgoZGF0YS5mcmFtZSgKICAgIGNsdXN0ZXI9IGFzLmNoYXJhY3RlcihhbGxfY29sJGNvbG9yW21hdGNoKHJvd25hbWVzKG15LndnbWF0W1tpXV0pLCBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSldKSwKICAgICIuIiA9ICJ3aGl0ZSIsCiAgICBzYW1wbGU9IGFzLmNoYXJhY3RlcihteS5jb2xzbVttYXRjaChhbGxfY29sJHNhbXBsZSwgbmFtZXMobXkuY29sc20pKV0pKQogICAgKQp9CgpybShteS5kYXRFeHByLCBteS5NRXMsIG15LmRjcykKYGBgCgojIyBzcGVhcm1hbiBjb3JyZWxhdGlvbiBoZWF0bWFwCgojIyMgYW5ub3RhdGlvbnMKCmBgYHtyIGFubm90LWxpc3R9CgojIG5hbWVzIGFuZCBjb2xvcnMgZm9yIHRoZSBoZWF0bWFwIGFubm90YXRpb24KYW5ub3RfbmFtZSA8LSBkYXRhLmZyYW1lKAogICJDZWxsdHlwZXMiID0gYWxsX2NvbCRzYW1wbGVfY2VsbHR5cGUsCiAgIlNhbXBsZSIgICAgPSBhbGxfY29sJHNhbXBsZSwKICByb3cubmFtZXMgPSBhbGxfY29sJHNhbXBsZV9jZWxsdHlwZSkKICAKCnBoZWF0X2NvbF90YWJsZSA8LSBkby5jYWxsKHJiaW5kLCBjb2xfdGFibGUpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oInNhbXBsZSIpICU+JSAKICBtdXRhdGUoc2FtcGxlID0gc3Vic3RyKHNhbXBsZSwgMSwxMSkpICU+JSAKICBtdXRhdGUoZmluZSA9IHBhc3RlKHNhbXBsZSwgZmluZSwgc2VwID0gIl8iKSkKCiMgbWF0Y2ggY29sb3IgdGFibGUgd2l0aCBhbm5vdGF0aW9uCnBoZWF0X2NvbF90YWJsZSA8LSBwaGVhdF9jb2xfdGFibGVbbWF0Y2goYW5ub3RfbmFtZSRDZWxsdHlwZXMsIHBoZWF0X2NvbF90YWJsZSRmaW5lKSxdCgphbm5vdF9jb2wgPC0gbGlzdCgKICBDZWxsdHlwZXMgPSBwaGVhdF9jb2xfdGFibGUkY29sb3IsCiAgU2FtcGxlID0gYyhHZ19EMDVfY3RybCA9ICIjQTRBNEE0IiwKICAgICAgICAgICAgIEdnX0QwN19jdHJsID0gIiM1MTUxNTEiLAogICAgICAgICAgICAgR2dfRDEwX2N0cmwgPSAiIzAwMDAwMCIpCiAgKQoKbmFtZXMoYW5ub3RfY29sW1sxXV0pIDwtIGFubm90X25hbWUkQ2VsbHR5cGVzCgpgYGAKCmBgYHtyLCAgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEzfQpoZWF0X2NvbCA8LSBjb2xvclJhbXBQYWxldHRlKGNvbG9ycyA9IGMoImRvZGdlcmJsdWU0IiwiZG9kZ2VyYmx1ZSIsICJ3aGl0ZSIsICJyZWQiLCAiZGFya3JlZCIpKQoKdG9wbG90IDwtIGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKQoKI0NhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgMS1jb3IsIGFuZCB0aGVuIGNhbGN1bGF0ZSBkZW5kb2dyYW1zIHRvIGNsdXN0ZXIgdGhlIGNsdXN0ZXJzCm15LmhjbHVzdCA9IGhjbHVzdChhcy5kaXN0KDEtY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpKSkKCiMgbG93ZXIgbGltaXQgdG8gc2NhbGUgY29sb3IgYmFyCmxvd19saW1pdCA8LSAxMDAgLSBhYnMocm91bmQocmFuZ2UodG9wbG90KVsxXSoxMDApKQoKcGhlYXRtYXAodG9wbG90LHJldkMgPSBULAogICAgICAgICAgICAgICAgIG1haW4gPSAic3BlYXJtYW4gY29ycmVsYXRpb24gb2YgYXZlcmFnZSBtb2R1bGUgZWlnZW5nZW5lIGV4cHJlc3Npb24gYnkgY2x1c3RlclxuIERlbmRyb2dyYW0gYmFzZWQgb24gJ2Rpc3RhbmNlIDEtY29yJyIsCiAgICAgICAgICAgICAgICAgZm9udHNpemUgPSA4LCAgCiAgICAgICAgICAgICAgICAgY29sb3IgPSBoZWF0X2NvbCgyMDApW2xvd19saW1pdDoyMDBdLAogICAgICAgICAgICAgICAgIHNob3dfY29sbmFtZXMgPSBGLAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IG15LmhjbHVzdCwKICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHMgPSBteS5oY2x1c3QsCiAgICAgICAgICAgICAgICAgdHJlZWhlaWdodF9yb3cgPSAwLCAKICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90X25hbWUsCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdF9jb2wsCiAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9sZWdlbmQgPSBGLAogICAgICAgICAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BCiAgICAgICAgICAgICAgICAgKQoKY29sYmFyIDwtIGMobWluKGNvcih0KG15LndnbWF0W1sxXV0pLCBtZXRob2QgPSAic3BlYXJtYW4iKSksCiAgICAgICAgICAgIG1heChjb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpKQoKI0NhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgMS1jb3IsIGFuZCB0aGVuIGNhbGN1bGF0ZSBkZW5kb2dyYW1zIHRvIGNsdXN0ZXIgdGhlIGNsdXN0ZXJzCm15LmhjbHVzdCA9IGFzLmRlbmRyb2dyYW0oaGNsdXN0KGFzLmRpc3QoMS1jb3IodChteS53Z21hdFtbMV1dKSwgbWV0aG9kID0gInNwZWFybWFuIikpKSkKCiNQbG90IQpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9tb2R1bGVfZ2VuZV9oZWF0bWFwX3NwZWFybWFuLnBkZiIsIHdpZHRoID0gMTEsIGhlaWdodCA9IDExKQpoZWF0bWFwLjQoY29yKHQobXkud2dtYXRbWzFdXSksIG1ldGhvZCA9ICJzcGVhcm1hbiIpLAogICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkobiA9IDEwMDApLAogICAgICAgICAgdHJhY2U9Im5vbmUiLAogICAgICAgICAgbGhlaT1jKDAuMiwxLjYpLAogICAgICAgICAgbHdpZD1jKDAuMiwxLjYpLAogICAgICAgICAgc2NhbGUgPSAibm9uZSIsbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgY2V4Q29sID0gMC43NSwKICAgICAgICAgIHJldkMgPSBUUlVFLAogICAgICAgICAgQ29sU2lkZUNvbG9ycyA9IG15LmhlYXRjb2xzW1sxXV0sCiAgICAgICAgICBSb3dTaWRlQ29sb3JzID0gdChteS5oZWF0Y29sc1tbMV1dWywzOjFdKSxSb3d2ID0gbXkuaGNsdXN0LCBDb2x2ID0gbXkuaGNsdXN0KQoKCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX21vZHVsZV9nZW5lX2hlYXRtYXBfY29sb3Jfa2V5X3NwZWFybWFuLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwKQojIGhlYXRtYXAgc2luY2UgY29sb3Iga2V5IGlzIG1pc3NpbmcgaW4gZmlyc3QgaGVhdG1hcApoZWF0bWFwLjQocmJpbmQoY29sYmFyLCBjb2xiYXIpLAogICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkobiA9IDEwMDApLAogICAgICAgICAgdHJhY2U9Im5vbmUiLAogICAgICAgICAgbGhlaT1jKDAuMiwxLjYpLAogICAgICAgICAgbHdpZD1jKDAuMiwxLjYpLAogICAgICAgICAgc2NhbGUgPSAibm9uZSIsbWFyZ2lucyA9IGMoMTAsMTApLAogICAgICAgICAgY2V4Q29sID0gMC43NSkKCmBgYAoKIyMgaGVhdG1hcCBvZiBtb2R1bGUgcHNldWRvYnVsayBhdmVyYWdlIGV4cHJlc3Npb24KCmBgYHtyLCAgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEwfQojIG1vZHVsZSBjb2xvcnMKbXkuY29sY29scyA9IGxpc3QoKQoKZm9yIChpIGluIDE6bGVuZ3RoKG15LndnbWF0KSkgewogICAgbXkuY29sY29sc1tbaV1dID0gYXMubWF0cml4KG5hbWVzKHRhYmxlKFdHQ05BX2RhdGFbW2ldXSRkeW5hbWljQ29scykpKQp9CgphdmcubW9kLmVpZ2VuZ2VuZXMgPC0gV0dDTkFfZGF0YVtbMV1dJHNjLk1FTGlzdCRhdmVyYWdlRXhwcgpybShXR0NOQV9kYXRhKQoKaHRtcCA8LSBoZWF0bWFwLjQoYXMubWF0cml4KG15LndnbWF0W1sxXV0pLAogICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkobiA9IDEwMDApLAogICAgICAgICAgdHJhY2U9Im5vbmUiLAogICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgIG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIENvbFNpZGVDb2xvcnMgPSBteS5jb2xjb2xzW1sxXV0sIAogICAgICAgICAgUm93U2lkZUNvbG9ycyA9IHQobXkuaGVhdGNvbHNbWzFdXSkpCgptb2R1bGVfb3JkZXIgPC0gaHRtcFtbImNvbEluZCJdXQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRGV2ZWxfbW9kdWxlX3ZfY2x1c3RlcnNfaGVhdG1hcC5wZGYiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDEwKQpoZWF0bWFwLjQoYXMubWF0cml4KG15LndnbWF0W1sxXV0pLAogICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJkb2RnZXJibHVlNCIsImRvZGdlcmJsdWUiLCAid2hpdGUiLCAicmVkIiwgImRhcmtyZWQiKSkobiA9IDEwMDApLAogICAgICAgICAgdHJhY2U9Im5vbmUiLAogICAgICAgICAgc2NhbGUgPSAicm93IiwKICAgICAgICAgIG1hcmdpbnMgPSBjKDEwLDEwKSwKICAgICAgICAgIENvbFNpZGVDb2xvcnMgPSBteS5jb2xjb2xzW1sxXV0sIAogICAgICAgICAgUm93U2lkZUNvbG9ycyA9IHQobXkuaGVhdGNvbHNbWzFdXSkpCgpgYGAKCgoKIyBMb2FkIHRoZSBpbnRlZ3JhdGVkIGRhdGEgc2V0CgpUaGUgaW50ZWdyYXRlZCBkYXRhIHNldCBvbiB3aGljaCB0aGUgV0dDTkEgaXMgY2FsY3VsYXRlZC4gV2UgcGxvdCBpdCwgc3BsaXQgYnkgdGhlIG9yaWdpbmFsIGNlbGwgdHlwZXMgZnJvbSB0aGUgdGhyZWUgb3JpZ2luYWwgc2FtcGxlcy4gCgpgYGB7ciBkaW1wbG90cywgZmlnLndpZHRoPTEwfQojIFRoaXMgaXMgYSBmaWxlIG9mIGFsbCB0aGUgY29tYmluZWQgbW91c2UgZGF0YXNldHMsIG5vcm1hbGl6ZWQgYW5kIHN1Y2guCm15LnNlYyA9IHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19kZXZlbF9pbnRfc2V1cmF0XzI1MDcyMy5yZHMiKQoKaWRlbnRpY2FsKHJvd25hbWVzKG15Lm1ldGFtKSwgY29sbmFtZXMobXkuc2VjKSkKCiNXaGljaCBXR0NOQSBkYXRhc2V0IGFyZSB3ZSB1c2luZz8KbXkuVyA9IDEKCiNDaG9vc2UgaG93IG1hbnkgZ3JvdXBzIG9mIGNlbGwgY2x1c3RlcnMgd2Ugd2FudCB0byB1c2UuIEJhc2VkIG9uIHRoZSBkaXN0YW5jZSBjbHVzdGVyaW5nIGZyb20gYWJvdmUKbXkuY2xjbCA9IGN1dHJlZShoY2x1c3QoYXMuZGlzdCgxLWNvcih0KG15LndnbWF0W1tteS5XXV0pKSkpLCBrID0gNSkKCm15Lm1ldGFtJHNhbXBsZV9jZWxsdHlwZSA8LSBmYWN0b3IobXkubWV0YW0kc2FtcGxlX2NlbGx0eXBlLCBsZXZlbHMgPSBuYW1lcyhteS5jbGNsKSkKI1NldCB0aGUgaWRlbnRpdGllcyBvZiB0aGUgaW50ZWdyYXRlZCBkYXRhLCB0byB0aGUgYW5ub3RhdGVkIGNsdXN0ZXJzCm15LnNlYyA9IFNldElkZW50KG15LnNlYywgdmFsdWUgPSBteS5tZXRhbSRzYW1wbGVfY2VsbHR5cGUpCgpwMSA8LSBEaW1QbG90KAogIG15LnNlYywKICByZWR1Y3Rpb24gPSAidHNuZSIsCiAgbGFiZWwgPSBUUlVFLAogIHJlcGVsID0gVFJVRSwKICBjb2xzID0gbXkuaGVhdGNvbHNbWzFdXVssImNsdXN0ZXIiXSwKICBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IgogICkgKyAKICBOb0xlZ2VuZCgpCgpwMQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRGV2ZWxfc3BsaXRfdHNuZS5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDE1KQojUGxvdCBzcGxpdCB0c25lCnAxCmBgYAoKIyBBdmcuIG1vZHVsZSBleHAuIGJ5IHN0YWdlCgp0U05FIERpbVBsb3RzIHNob3dpbmcgdGhlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBvZiBlYWNoIG1vZHVsZSBieSBzdGFnZS4KCmBgYHtyIEFFLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NDB9Cgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAjIHByZXBhcmUgYXZlcmFnZSBleHByZXNzaW9uIHRhYmxlCiAgdG1wIDwtIGF2Zy5tb2QuZWlnZW5nZW5lcyAlPiUKICAgIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJjZWxsX0lEIikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGdyZXBsKHBhc3RlMCgiXyIsIGksICIkIiksIGNlbGxfSUQpKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoY2VsbF9JRCA9IHN0cmluZ3I6OnN0cl9yZW1vdmVfYWxsKGNlbGxfSUQsIHBhc3RlMCgiXyIsIGkpKSkgJT4lCiAgICB0aWJibGU6OmNvbHVtbl90b19yb3duYW1lcygiY2VsbF9JRCIpCiAgCiAgaWRlbnRpY2FsKHJvd25hbWVzKHRtcCksIGNvbG5hbWVzKG15LnNlc1tbaV1dKSkKICAjIGFkZCBtZXRhIGRhdGEgdG8gdGhlIHNldXJhdCBvYmplY3RzCiAgbXkuc2VzW1tpXV0gPC0gQWRkTWV0YURhdGEobXkuc2VzW1tpXV0sIHRtcCkKfQoKI21heCBhbmQgbWluIGV4cHJlc3Npb24gcGVyIG1vZHVsZSAoY29sdW1uIG1heCkKbW9kX21heCA8LSBhcHBseShhdmcubW9kLmVpZ2VuZ2VuZXMsIE1BUkdJTiA9IDIsIEZVTiA9IG1heClbbW9kdWxlX29yZGVyXQptb2RfbWluIDwtIGFwcGx5KGF2Zy5tb2QuZWlnZW5nZW5lcywgTUFSR0lOID0gMiwgRlVOID0gbWluKVttb2R1bGVfb3JkZXJdCgptb2RwbG90cyA8LSBsaXN0KCkKbW9kcGxvdHNbWzFdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzJdXSA8LSBsaXN0KCkKbW9kcGxvdHNbWzNdXSA8LSBsaXN0KCkKCm1vZHVsZXNfaW5fb3JkZXIgPC0gY29sbmFtZXModG1wKVttb2R1bGVfb3JkZXJdCgojIHBsb3QgdGhlIG1vZHVsZXMgc3BsaXQgdG8gdGhlIHN0YWdlcwpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICBmb3IgKGogaW4gc2VxKG5jb2wodG1wKSkpIHsKICAKICAgIG1vZHBsb3RzW1tpXV1bW2pdXSAgPC0gRmVhdHVyZVBsb3QoCiAgICAgIG15LnNlc1tbaV1dLAogICAgICBmZWF0dXJlcyA9IG1vZHVsZXNfaW5fb3JkZXJbal0sCiAgICAgIHJlZHVjdGlvbiA9ICJ0c25lIiwKICAgICAgY29scyA9IGMoImdyZXk5MCIsIHN1YnN0cmluZyhtb2R1bGVzX2luX29yZGVyW2pdLCAzKSkKICAgICAgKSArCiAgICAgIGdndGl0bGUoc3RyaW5ncjo6c3RyX3JlbW92ZShtb2R1bGVzX2luX29yZGVyW2pdLCJeQUUiKSkgKwogICAgICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3c9Iml2b3J5MiIsIGhpZ2g9c3Vic3RyaW5nKG1vZHVsZXNfaW5fb3JkZXJbal0sIDMpLCAjY29sb3JzIGluIHRoZSBzY2FsZQogICAgICAgICAgICAgICAgICMgYnJlYWtzPXNlcShtb2RfbWluW2pdLCBtb2RfbWF4W2pdLCAwLjEpLCAjYnJlYWtzIGluIHRoZSBzY2FsZSBiYXIKICAgICAgICAgICAgICAgICBsaW1pdHM9Yyhtb2RfbWluW2pdLCBtb2RfbWF4W2pdKSkgI3NhbWUgbGltaXRzIGZvciBwbG90cwoKICAgIAogICAgfQp9Cgp0bXAgPC0gYyhtb2RwbG90c1tbMV1dLCBtb2RwbG90c1tbMl1dLCBtb2RwbG90c1tbM11dKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSB0bXAsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRGV2ZWxfbW9kdWxlc19BRV9wbG90cy5wZGYiLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA3MCkKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ3JvYnMgPSB0bXAsIG5jb2wgPSAzLCBhcy50YWJsZSA9IEZBTFNFKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfTU5fbW9kX3RzbmUucGRmIiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMikKbW9kcGxvdHNbWzFdXVtbMjJdXSArIG1vZHBsb3RzW1sxXV1bWzZdXSB8CiAgbW9kcGxvdHNbWzJdXVtbMjJdXSArIG1vZHBsb3RzW1syXV1bWzZdXSB8CiAgbW9kcGxvdHNbWzNdXVtbMjJdXSArIG1vZHBsb3RzW1szXV1bWzZdXQoKYGBgCgojIEFFIG92ZXIgdGltZQoKV2UgcGxvdCB0aGUgYXZlcmFnZSBleHByZXNzaW9uIG9mIGVhY2ggbW9kdWxlIGluIHRoZSB0aHJlZSBzdGFnZXMgYW5kIHRoZSA1IGJyb2FkIGNlbGwgdHlwZSBjbHVzdGVycyBwcmVzZW50IGluIGFsbCAzIHN0YWdlcy4KCmBgYHtyIEFFLW92ZXItdGltZSwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9MTJ9CiMgbW9kdWxlIGFubm90YXRpb25zCm1vZF9hbm5vdCA8LSByZWFkLmNzdigifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9HZ19kZXZlbF9pbnRfc2NXR0NOQV9tb2R1bGVfYW5ub3RhdGlvbi5jc3YiKSAlPiUKICBkcGx5cjo6bXV0YXRlKG1vZHVsZSA9IHN0cl9yZXBsYWNlX2FsbChtb2R1bGUsICJcXGR7MSwyfVxcXyIsICJBRSIpKQoKbWV0YSA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG1ldGFbW2ldXSA8LSBteS5zZXNbW2ldXUBtZXRhLmRhdGEgJT4lCiAgICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiY2VsbF9JRCIpICU+JQogICAgZHBseXI6Om11dGF0ZShjZWxsX0lEID0gcGFzdGUwKGNlbGxfSUQsICJfIiwgaSkpICU+JQogICAgZHBseXI6OnNlbGVjdChjKCJjZWxsX0lEIiwgImJyb2FkIikpCn0KCm1ldGEgPC0gZG8uY2FsbChyYmluZCwgbWV0YSkKCiMgbWVhbiBhdmVyYWdlIGV4cHJlc3Npb24gYnkgc3RhZ2UKbWVhbl9BRSA8LSBhdmcubW9kLmVpZ2VuZ2VuZXMgJT4lCiAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oImNlbGxfSUQiKSAlPiUKICBkcGx5cjo6bXV0YXRlKHN0YWdlID0gc3RyaW5ncjo6c3RyX3N1YihjZWxsX0lELCAtMSkpICU+JQogIGRwbHlyOjptdXRhdGUoc3RhZ2UgPSBmYWN0b3Ioc3RhZ2UsIGxldmVscyA9IGMoMTozKSwgbGFiZWxzID0gYygiRDA1IiwgIkQwNyIsICJEMTAiKSkpICU+JQogIGRwbHlyOjpsZWZ0X2pvaW4obWV0YSwgYnkgPSAiY2VsbF9JRCIpICU+JQogIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKCJjZWxsX0lEIikgJT4lCiAgdGlkeXI6OnVuaXRlKCJzdGFnZV9jbCIsIHN0YWdlLCBicm9hZCwgc2VwID0gIl8iKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoc3RhZ2VfY2wpICU+JQogIGRwbHlyOjpzdW1tYXJpc2VfZWFjaChtZWFuKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGdhdGhlcihrZXk9Im1vZHVsZSIsIHZhbHVlID0gIkFFIiwgLXN0YWdlX2NsKSAlPiUKICBkcGx5cjo6bGVmdF9qb2luKG1vZF9hbm5vdFssIGMoMSwzKV0sIGJ5ID0gIm1vZHVsZSIpICU+JQogIHRpZHlyOjpzZXBhcmF0ZSgic3RhZ2VfY2wiLCBjKCJzdGFnZSIsICJicm9hZCIpLCBzZXAgPSAiXyIsIHJlbW92ZSA9IEZBTFNFKSAKIyAlPiUKICAjIGRwbHlyOjpmaWx0ZXIoYnJvYWQgJWluJSBjKCJwcm9nZW5pdG9ycyIsICJuZXVyb25zIiwgIlJQIiwgIkZQIiwgInBlcmljeXRlcyIpKQoKbGFiZWxzX2RvdHBsb3QgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShtb2R1bGVzX2luX29yZGVyLCAiXkFFIikKbmFtZXMobGFiZWxzX2RvdHBsb3QpIDwtIG1vZHVsZXNfaW5fb3JkZXIKCnBsb3RfY2x1c3RlcnMgPC0gYygicHJvZ2VuaXRvcnMiLCAibmV1cm9ucyIsICJSUCIsICJGUCIsICJwZXJpY3l0ZXMiLCAiT1BDIiwgIk1GT0wiLCAibWljcm9nbGlhIiwgImJsb29kIikKCm1lYW5fbW9kIDwtIGdncGxvdChkYXRhID0gbWVhbl9BRSwKICBhZXMoCiAgICB4ID0gc3RhZ2UsCiAgICB5ID0gQUUsCiAgICBjb2xvciA9IGZhY3Rvcihicm9hZCwgbGV2ZWxzID0gcGxvdF9jbHVzdGVycyksCiAgICBncm91cCA9IGJyb2FkLAogICAgbGFiZWwgPSBhbm5vdGF0aW9uCiAgICApCiAgKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNsdXN0X2NvbCRjb2xvclttYXRjaChwbG90X2NsdXN0ZXJzLCBjbHVzdF9jb2wkYnJvYWQpXSkgKwogIHRoZW1lX2J3KCkgKwogICMgZmFjZXQgd3JhcCB3aXRoIHJlb3JkZXJlZCBmYWN0b3JzCiAgZmFjZXRfd3JhcCh2YXJzKGZhY3Rvcihtb2R1bGUsIGxldmVscyA9IHVuaXF1ZShtZWFuX0FFJG1vZHVsZSlbbW9kdWxlX29yZGVyXSkpLAogICAgICAgICAgICAgc2NhbGVzID0gImZyZWVfeSIsCiAgICAgICAgICAgICBucm93ID0gNCwKICAgICAgICAgICAgIG5jb2wgPSA2KSArCiAgbGFicyhjb2xvciA9ICJicm9hZCIpICsKICBnZ3RpdGxlKCJBdmVyYWdlIG1vZHVsZSBleHByZXNzaW9uIGJ5IHN0YWdlIikKCnBsb3RseTo6Z2dwbG90bHkobWVhbl9tb2QpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9tZWFuX21vZF9BRS5wZGYiLCBoZWlnaHQgPSA3LCB3aWR0aCA9IDEyKQojUGxvdCBzcGxpdCB0c25lCm1lYW5fbW9kCmBgYAoKIyBWbG5QbG90cyBvZiBhdmcuIG1vZHVsZSBleHAuIGJ5IHN0YWdlIGFuZCBzZXVyYXQgY2x1c3RlciAKCiMjIGNvbG9yZWQgYnkgbW9kdWxlCgpgYGB7ciBhdmVyYWdlLW1vZHVsZS1leHByZXNzaW9uLXBlci1jbHVzdGVyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MjB9CgojIHJlb3JkZXIgc2V1cmF0IGNsdXN0ZXJzCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIG15LnNlc1tbaV1dJHNldXJhdF9jbHVzdGVycyA8LSBmYWN0b3IoCiAgbXkuc2VzW1tpXV0kc2V1cmF0X2NsdXN0ZXJzLAogIGxldmVscyA9IGxldmVscyhteS5zZXNbW2ldXSRzZXVyYXRfY2x1c3RlcnMpW2FzLmludGVnZXIoc3RyX2V4dHJhY3Qob3JkX2xldmVsc1tbaV1dLCAiXFxkezEsMn0kIikpXQogICkKCn0KCnZwbG90cyA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobXkuc2VzKSkgewogIAogIG1vZHMgPC0gY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKVtncmVwKCJeQUUiLGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSkpXQogIAogIHZwbG90c1tbaV1dIDwtIFZsblBsb3QoCiAgICAgICAgbXkuc2VzW1tpXV0sCiAgICAgICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgICAgIGNvbHMgPSBzdWJzdHJpbmcobW9kcywgMylbbW9kdWxlX29yZGVyXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKICAKfQoKdnBsb3RzW1sxXV0KdnBsb3RzW1syXV0KdnBsb3RzW1szXV0KCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ18xX0FFX2J5X2NsdXN0ZXJfbW9kY29sLnBkZiIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDEwKQp2cGxvdHNbWzFdXQp2cGxvdHNbWzJdXQp2cGxvdHNbWzNdXQpgYGAKCiMjIGNvbG9yZWQgYnkgY2VsbCB0eXBlCgpgYGB7ciB2bG4tYnktY2x1c3RlciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTIwfQpjbHVzdF9jb2wgPC0gcmVhZC5jc3YoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvYnJvYWRfY2x1c3Rlcl9tYXJrZXJfY29sb3JzLmNzdiIpCgp2cGxvdHNfaWQgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKG15LnNlcykpIHsKICAgIAogIG1vZHMgPC0gY29sbmFtZXMobXkuc2VzW1tpXV1bW11dKVtncmVwKCJeQUUiLGNvbG5hbWVzKG15LnNlc1tbaV1dW1tdXSkpXQogIAogIHZwbG90c19pZFtbaV1dIDwtIFZsblBsb3QoCiAgICBteS5zZXNbW2ldXSwKICAgIGZlYXR1cmVzID0gbW9kc1ttb2R1bGVfb3JkZXJdLAogICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgIHN0YWNrID0gVFJVRSwgZmxpcCA9IFRSVUUsCiAgICBmaWxsLmJ5ID0gImlkZW50IiwKICAgIGNvbHMgPSBjb2xfdGFibGVbW2ldXSRjb2xvclthcy5pbnRlZ2VyKHN0cl9leHRyYWN0KG9yZF9sZXZlbHNbW2ldXSwgIlxcZHsxLDJ9JCIpKV0pICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbHR5ID0gImRhc2hlZCIpCn0KCnZwbG90c19pZFtbMV1dCnZwbG90c19pZFtbMl1dCnZwbG90c19pZFtbM11dCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9jbHVzdGVyX2NsdWNvbC5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAxMCkKdnBsb3RzX2lkW1sxXV0KdnBsb3RzX2lkW1syXV0KdnBsb3RzX2lkW1szXV0KYGBgCgojIE1OIG1vZHVsZXMKCkZvciB0aGUgZmlndXJlcyB3ZSBzcGVjaWZpY2FsbHkgc2VsZWN0IHRoZSB0d28gTU4gbW9kdWxlcyBhbmQgcGxvdCB0aGVtIGFzIFZsbiBwbG90cy4KCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9NX0KdG1wIDwtIGF2Zy5tb2QuZWlnZW5nZW5lcyAKICAKaWRlbnRpY2FsKHJvd25hbWVzKHRtcCksIGNvbG5hbWVzKG15LnNlYykpCiMgYWRkIG1ldGEgZGF0YSB0byB0aGUgaW50IHNldXJhdCBvYmplY3QKbXkuc2VjIDwtIEFkZE1ldGFEYXRhKG15LnNlYywgdG1wKQpteS5zZWMgPC0gQWRkTWV0YURhdGEobXkuc2VjLCBteS5tZXRhbVtjKCJmaW5lIiwgInNhbXBsZV9jZWxsdHlwZSIpXSkKCmN1c3RvbV9vcmRlciA8LSBjKHBhc3RlKG5hbWVzKG9yZF9sZXZlbHMpWzFdLCBvcmRfbGV2ZWxzW1sxXV0sIHNlcCA9ICdfJyksCiAgICAgICAgICAgICAgICAgIHBhc3RlKG5hbWVzKG9yZF9sZXZlbHMpWzJdLCBvcmRfbGV2ZWxzW1syXV0sIHNlcCA9ICdfJyksCiAgICAgICAgICAgICAgICAgIHBhc3RlKG5hbWVzKG9yZF9sZXZlbHMpWzNdLCBvcmRfbGV2ZWxzW1szXV0sIHNlcCA9ICdfJykpCgpteS5zZWMkc2FtcGxlX2NlbGx0eXBlIDwtIGZhY3RvcigKICBteS5zZWMkc2FtcGxlX2NlbGx0eXBlLAogIGxldmVscyA9IGN1c3RvbV9vcmRlcgogICkKCnZsbl9pbmQgPC0gVmxuUGxvdChteS5zZWMsCiAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IGMoIkFFZGFya3JlZCIsICJBRWdyZWVuIiksCiAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgICAgICAgICAgICAgICAgc3RhY2sgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgZmxpcCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICBjb2xzID0gYygiZGFya3JlZCIsICJncmVlbiIpLHB0LnNpemUgPSAxCiAgICAgICAgICAgICAgICAgICkgKwogICAgTm9MZWdlbmQoKQoKdmxuX2luZApwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9NTnMucGRmIiwgaGVpZ2h0ID0gNywgd2lkdGggPSAyMCkKdmxuX2luZApgYGAKCmBgYHtyfQpWbG5QbG90KAogICAgbXkuc2VjLAogICAgZmVhdHVyZXMgPSBtb2RzW21vZHVsZV9vcmRlcl0sCiAgICBncm91cC5ieSA9ICJzYW1wbGVfY2VsbHR5cGUiLAogICAgc3RhY2sgPSBUUlVFLCBmbGlwID0gVFJVRSwKICAgIGNvbHMgPSBzdWJzdHJpbmcobW9kcywgMylbbW9kdWxlX29yZGVyXSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsdHkgPSAiZGFzaGVkIikKCgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzFfQUVfYnlfY2x1c3Rlcl9pbnRlZ3JhdGVkX2RhdGEucGRmIiwgaGVpZ2h0ID0gMjAsIHdpZHRoID0gMzApClZsblBsb3QoCiAgICBteS5zZWMsCiAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZV9jZWxsdHlwZSIsCiAgICBzdGFjayA9IFRSVUUsIGZsaXAgPSBUUlVFLAogICAgY29scyA9IHN1YnN0cmluZyhtb2RzLCAzKVttb2R1bGVfb3JkZXJdKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGx0eSA9ICJkYXNoZWQiKQoKCmBgYAoKIyBhdmcuIG1vZHVsZSBleHAuIGJ5IHN0YWdlCgpgYGB7ciBBRS1ieS1zdGFnZSwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQp2MSA8LSBWbG5QbG90KG15LnNlYywKICAgICAgICBmZWF0dXJlcyA9IG1vZHNbbW9kdWxlX29yZGVyXSwgCiAgICAgICAgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpCnYxCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfMV9BRV9ieV9zdGFnZS5wZGYiLCBoZWlnaHQgPSAyMCwgd2lkdGggPSAyMCkKdjEKCmBgYAoKIyBtb2R1bGUgR08gdGVybXMgYmFycGxvdHMKYGBge3J9CiMgTG9hZCB0aGUgdG9wIDUwIChieSBsaW1tYTo6dG9wR08pIEdvIHRlcm0gdGFibGUgbGlzdCBmcm9tIHNjV0dDTkFfR2dfZGV2ZWxfaW50LlJtZApnb3Rlcm1zIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvb3V0cHV0L0dnX2RldmVsX2ludF9tb2R1bGVfR09UZXJtc18yNTA3MjMucmRzIikKIyBMb2FkIHRoZSBkZXZlbCBtb2R1bGUgYW5ub3RhdGlvbnMgCm1vZHVsZXMgPC0gcmVhZC5jc3YoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvR2dfZGV2ZWxfaW50X3NjV0dDTkFfbW9kdWxlX2Fubm90YXRpb24uY3N2IikKIyBTZXQgbGlzdCBuYW1lcyBhcyBtb2R1bGUgbnVtYmVyIGFuZCBuYW1lIChlLmcuICIxX2JsYWNrIikKbmFtZXMoZ290ZXJtcykgPC0gbW9kdWxlcyRtb2R1bGUKCnBsb3RfbGlzdCA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEoZ290ZXJtcykpIHsKICAjIFNlbGVjdCB0aGUgZGFya3JlZCBtb2R1bGUgKG1vdG9yIG5ldXJvbi90cmFuc21lbWJyYW5lIHRyYW5zcG9ydCkKICB0b3Bsb3QgPC0gZ290ZXJtc1tbaV1dCiAgdGVybXMgPC0gYXMuY2hhcmFjdGVyKHRvcGxvdCRUZXJtKQogIAogICNQbG90IHRoZSBiYXJwbG90IQogIHBsb3RfbGlzdFtbaV1dIDwtIGdncGxvdCh0b3Bsb3QsIGFlcyh4PVRlcm0sIHk9LWxvZzEwKFAuREUpLCBmaWxsPU9udCkpICsKICAgIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gc3RyX3JlbW92ZShuYW1lcyhnb3Rlcm1zKVtpXSwgIl5cXGR7MSwyfV8iKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSkgKwogICAgbGFicyh0aXRsZT1wYXN0ZTAoIlRvcCBHT1Rlcm0gb2YgbW9kdWxlICIsIG5hbWVzKGdvdGVybXMpW2ldKSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsKICAgIHRoZW1lX2Nvd3Bsb3QoKQp9CgpybSh0b3Bsb3QsIHRlcm1zKQoKbmFtZXMocGxvdF9saXN0KSA8LSBtb2R1bGVzJG1vZHVsZQoKIyBFeHBvcnQgUERGCnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0dPX3Rlcm1fYmFycGxvdF9zY1dHQ05BX0dnX2RldmVsX21vZHVsZXMucGRmIiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gNykKZm9yIChpIGluIHNlcShnb3Rlcm1zKSkgewogIGdyaWQuYXJyYW5nZShwbG90X2xpc3RbW2ldXSkKfQpkZXYub2ZmKCkKCmBgYAoKCmBgYHtyfQojIERhdGUgYW5kIHRpbWUgb2YgUmVuZGVyaW5nClN5cy50aW1lKCkKCnNlc3Npb25JbmZvKCkKYGBgCgo=